/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package uk.co.samuelwall.materialtaptargetprompt.sample.weight.MaterButton;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Texture;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;
import uk.co.samuelwall.materialtaptargetprompt.sample.utils.NumUtil;
import uk.co.samuelwall.materialtaptargetprompt.sample.utils.ShapeUtil;
import uk.co.samuelwall.materialtaptargetprompt.sample.utils.Utils;

/**
 * Button
 *
 * @since 2021-06-21
 */
public abstract class Button extends CustomView
        implements Component.TouchEventListener, Component.FocusChangedListener {
    // Complete in child class
    int minWidth;
    int minHeight;
    float xfloat = NumUtil.FLOAT_F1;
    float yfloat = NumUtil.FLOAT_F1;
    float radius = NumUtil.FLOAT_F1;
    float rippleSpeed = NumUtil.FLOAT_12;
    int rippleSize = NumUtil.INT_3;
    String rippleColor;
    Component.ClickedListener onClickListener;
    boolean isClickAfterRipple = true;
    String backgroundColor = "#1E88E5FF";
    Context context;


    /**
     * Button
     *
     * @param context      context
     * @param attrs        attrs
     * @param defStyleAttr defStyleAttr
     */
    public Button(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        setDefaultProperties();
        if (attrs.getAttr("animate").isPresent()) {
            isClickAfterRipple = attrs.getAttr("animate").get().getBoolValue();
        }
        setAttributes(attrs);
        beforeBackground = backgroundColor;
        if (rippleColor == null) {
            rippleColor = makePressColorString();
        }
        setTouchEventListener(this);
        setFocusChangedListener(this);
    }

    /**
     * setDefaultProperties
     */
    protected void setDefaultProperties() {
        // Min size
        setMinHeight(Utils.dpToPx(minHeight, context));
        setMinWidth(Utils.dpToPx(minWidth, context));

        // Background shape
        setBackgroundColor(backgroundColor);
    }

    /**
     * setAttributes
     *
     * @param attrs attrs
     */
    protected abstract void setAttributes(AttrSet attrs);

    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
        MmiPoint mmiPoint = event.getPointerScreenPosition(event.getIndex());
        if (isEnabled()) {
            setLastTouch(true);
            if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
                radius = (float) (getHeight() / rippleSize);
                xfloat = Math.abs(mmiPoint.getX() - getLocationOnScreen()[0]);
                yfloat = Math.abs(mmiPoint.getY() - getLocationOnScreen()[1]);
            } else if (event.getAction() == TouchEvent.POINT_MOVE) {
                radius = (float) (getHeight() / rippleSize);
                xfloat = Math.abs(mmiPoint.getX() - getLocationOnScreen()[0]);
                yfloat = Math.abs(mmiPoint.getY() - getLocationOnScreen()[1]);
                if (!(Math.abs(mmiPoint.getX() - getLocationOnScreen()[0]) <= getWidth()
                        && Math.abs(mmiPoint.getY() - getLocationOnScreen()[1]) <= getHeight())) {
                    setLastTouch(false);
                    xfloat = NumUtil.FLOAT_F1;
                    yfloat = NumUtil.FLOAT_F1;
                }
            } else if (event.getAction() == TouchEvent.PRIMARY_POINT_UP) {
                if (Math.abs(mmiPoint.getX() - getLocationOnScreen()[0]) <= getWidth()
                        && Math.abs(mmiPoint.getY() - getLocationOnScreen()[1]) <= getHeight()) {
                    radius++;
                    if (!isClickAfterRipple && onClickListener != null) {
                        onClickListener.onClick(this);
                    }
                } else {
                    setLastTouch(false);
                    xfloat = NumUtil.FLOAT_F1;
                    yfloat = NumUtil.FLOAT_F1;
                }
            } else if (event.getAction() == TouchEvent.CANCEL) {
                setLastTouch(false);
                xfloat = NumUtil.FLOAT_F1;
                yfloat = NumUtil.FLOAT_F1;
            }
        }
        invalidate();
        return true;
    }

    @Override
    public void onFocusChange(Component component, boolean isGainFocus) {
        if (!isGainFocus) {
            xfloat = NumUtil.FLOAT_F1;
            yfloat = NumUtil.FLOAT_F1;
        }
    }

    /**
     * makeCircle
     *
     * @return PixelMap
     */
    public PixelMap makeCircle() {
        PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
        initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
        initializationOptions.size =
                new Size(getWidth() - Utils.dpToPx(NumUtil.INT_6, context),
                        getHeight() - Utils.dpToPx(NumUtil.INT_7, context));
        PixelMap pixelMap = PixelMap.create(initializationOptions);
        Texture texture = new Texture(pixelMap);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(new Color(Color.getIntColor(Utils.argb(rippleColor))));
        paint.setAlpha(NumUtil.FLOAT_03);
        Canvas canvas = new Canvas(texture);
        canvas.drawCircle(xfloat, yfloat, radius, paint);
        if (radius > (float) (getHeight() / rippleSize)) {
            radius += rippleSpeed;
        }
        if (radius >= getWidth()) {
            xfloat = NumUtil.FLOAT_F1;
            yfloat = NumUtil.FLOAT_F1;
            radius = (float) (getHeight() / rippleSize);
            if (onClickListener != null && isClickAfterRipple) {
                onClickListener.onClick(this);
            }
        }
        return texture.getPixelMap();
    }

    /**
     * Make a dark color to ripple effect
     *
     * @return 改变后的颜色值
     */
    protected String makePressColorString() {
        int colorInt;
        if (backgroundColor.length() == NumUtil.INT_7) {
            colorInt = Color.getIntColor(backgroundColor);
        } else {
            colorInt = Color.getIntColor(backgroundColor.substring(0, backgroundColor.length() - NumUtil.INT_2));
        }
        int r = (colorInt >> NumUtil.INT_16) & NumUtil.INT_0XFF;
        int g = (colorInt >> NumUtil.INT_8) & NumUtil.INT_0XFF;
        int b = (colorInt >> 0) & NumUtil.INT_0XFF;
        r = Math.max(r - NumUtil.INT_30, 0);
        g = Math.max(g - NumUtil.INT_30, 0);
        b = Math.max(b - NumUtil.INT_30, 0);
        StringBuilder builder =
                new StringBuilder("#")
                        .append(colorPrefix(Integer.toHexString(r)))
                        .append(colorPrefix(Integer.toHexString(g)))
                        .append(colorPrefix(Integer.toHexString(b)));
        return builder.toString();
    }

    private String colorPrefix(String string) {
        return string.length() > 1 ? string : "00";
    }

    @Override
    public void setClickedListener(Component.ClickedListener listener) {
        onClickListener = listener;
    }

    /**
     * setBackgroundColor
     *
     * @param color color
     */
    public void setBackgroundColor(String color) {
        this.backgroundColor = color;
        if (isEnabled()) {
            beforeBackground = backgroundColor;
        }
        setBackground(ShapeUtil.createDrawable(Color.getIntColor(Utils.rgba(color))));
    }

    public void setRippleSpeed(float rippleSpeed) {
        this.rippleSpeed = rippleSpeed;
    }

    public float getRippleSpeed() {
        return this.rippleSpeed;
    }

    /**
     * getYfloat
     *
     * @return float
     */
    public float getYfloat() {
        return yfloat;
    }

    /**
     * yfloat
     *
     * @param yfloat
     */
    public void setYfloat(float yfloat) {
        this.yfloat = yfloat;
    }
}
